// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "mt8167-gpio.h"

#include <fbl/auto_call.h>
#include <lib/fake_ddk/fake_ddk.h>
#include <lib/mmio/mmio.h>
#include <mock-mmio-reg/mock-mmio-reg.h>

namespace {
constexpr size_t kGpioRegCount = MT8167_GPIO_SIZE / sizeof(uint16_t);
constexpr size_t kIoCfgRegCount = MT8167_IOCFG_SIZE / sizeof(uint16_t);
}  // namespace

namespace gpio {

class Mt8167GpioDeviceTest : public Mt8167GpioDevice {
 public:
  explicit Mt8167GpioDeviceTest(ddk_mock::MockMmioRegRegion& gpio_regs,
                                ddk_mock::MockMmioRegRegion& registers1,
                                ddk_mock::MockMmioRegRegion& registers2)
      : Mt8167GpioDevice(nullptr, gpio_regs.GetMmioBuffer(), registers1.GetMmioBuffer(),
                         registers2.GetMmioBuffer()) {
    // Fake interrupts fooling GPIO's checks, not used in these tests.
    interrupts_ = fbl::Array(new zx::interrupt[MT8167_GPIO_EINT_MAX], MT8167_GPIO_EINT_MAX);
  }
};

TEST(GpioTest, NoPull0) {
  fbl::Array<ddk_mock::MockMmioReg> gpio_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kGpioRegCount], kGpioRegCount);
  ddk_mock::MockMmioRegRegion gpios_mock(gpio_regs.data(), sizeof(uint16_t), kGpioRegCount);
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), kGpioRegCount);
  Mt8167GpioDeviceTest gpio(gpios_mock, unused, unused);

  gpios_mock[0x300].ExpectRead(0xFFFF).ExpectWrite(0xFFF8);  // 3 GPIO_MODE1 bits for GPIO mode.
  gpios_mock[0x000].ExpectRead(0xFFFF).ExpectWrite(0xFFFE);  // GPIO_DIR1 bit 0 to input (0).
  gpios_mock[0x500].ExpectRead(0xFFFF).ExpectWrite(0xFFFE);  // GPIO_PULLEN1 bit 0 to disabled (0).
  EXPECT_OK(gpio.GpioImplConfigIn(0, GPIO_NO_PULL));
  gpios_mock.VerifyAll();
}

TEST(GpioTest, NoPullMid) {
  fbl::Array<ddk_mock::MockMmioReg> gpio_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kGpioRegCount], kGpioRegCount);
  ddk_mock::MockMmioRegRegion gpios_mock(gpio_regs.data(), sizeof(uint16_t), kGpioRegCount);
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), kGpioRegCount);
  Mt8167GpioDeviceTest gpio(gpios_mock, unused, unused);

  gpios_mock[0x3D0].ExpectRead(0xFFFF).ExpectWrite(0xFE3F);  // 3 GPIO_MODEE bits for GPIO mode.
  gpios_mock[0x040].ExpectRead(0xFFFF).ExpectWrite(0xFFF7);  // GPIO_DIR5 bit 0 to input (0).
  gpios_mock[0x540].ExpectRead(0xFFFF).ExpectWrite(0xFFF7);  // GPIO_PULLEN5 bit 0 to disabled (0).
  EXPECT_OK(gpio.GpioImplConfigIn(67, GPIO_NO_PULL));
  gpios_mock.VerifyAll();
}

TEST(GpioTest, NoPullHigh) {
  fbl::Array<ddk_mock::MockMmioReg> gpio_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kGpioRegCount], kGpioRegCount);
  ddk_mock::MockMmioRegRegion gpios_mock(gpio_regs.data(), sizeof(uint16_t), kGpioRegCount);
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), kGpioRegCount);
  Mt8167GpioDeviceTest gpio(gpios_mock, unused, unused);

  gpios_mock[0x480].ExpectRead(0xFFFF).ExpectWrite(0x8FFF);  // 3 GPIO_MODE19 bits for GPIO mode.
  gpios_mock[0x070].ExpectRead(0xFFFF).ExpectWrite(0xEFFF);  // GPIO_DIR8 bit 0 to input (0).
  gpios_mock[0x570].ExpectRead(0xFFFF).ExpectWrite(0xEFFF);  // GPIO_PULLEN8 bit 0 to disabled (0).
  EXPECT_OK(gpio.GpioImplConfigIn(124, GPIO_NO_PULL));
  gpios_mock.VerifyAll();
}

TEST(GpioTest, OutOfRange) {
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), kGpioRegCount);
  Mt8167GpioDeviceTest gpio(unused, unused, unused);

  EXPECT_EQ(gpio.GpioImplConfigIn(kGpioRegCount, GPIO_NO_PULL), ZX_ERR_INVALID_ARGS);
}

TEST(GpioTest, PullUp) {
  fbl::Array<ddk_mock::MockMmioReg> gpio_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kGpioRegCount], kGpioRegCount);
  ddk_mock::MockMmioRegRegion gpios_mock(gpio_regs.data(), sizeof(uint16_t), kGpioRegCount);
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), kGpioRegCount);
  Mt8167GpioDeviceTest gpio(gpios_mock, unused, unused);

  gpios_mock[0x360].ExpectRead(0xFFFF).ExpectWrite(0xF1FF);  // 3 GPIO_MODE7 bits for GPIO mode.
  gpios_mock[0x020].ExpectRead(0xFFFF).ExpectWrite(0xFFFD);  // GPIO_DIR3 bit 0 to input (0).
  gpios_mock[0x520].ExpectRead(0x0000).ExpectWrite(0x0002);  // GPIO_PULLEN3 bit 0 to enable (1).
  gpios_mock[0x620].ExpectRead(0x0000).ExpectWrite(0x0002);  // GPIO_PULLSEL3 bit 0 to up (1).
  EXPECT_OK(gpio.GpioImplConfigIn(33, GPIO_PULL_UP));
  gpios_mock.VerifyAll();
}

TEST(GpioTest, PullDown) {
  fbl::Array<ddk_mock::MockMmioReg> gpio_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kGpioRegCount], kGpioRegCount);
  ddk_mock::MockMmioRegRegion gpios_mock(gpio_regs.data(), sizeof(uint16_t), kGpioRegCount);
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), kGpioRegCount);
  Mt8167GpioDeviceTest gpio(gpios_mock, unused, unused);

  gpios_mock[0x360].ExpectRead(0xFFFF).ExpectWrite(0xFE3F);  // 3 GPIO_MODE7 bits for GPIO mode.
  gpios_mock[0x020].ExpectRead(0xFFFF).ExpectWrite(0xFFFE);  // GPIO_DIR3 bit 0 to input (0).
  gpios_mock[0x520].ExpectRead(0x0000).ExpectWrite(0x0001);  // GPIO_PULLEN3 bit 0 to enable (1).
  gpios_mock[0x620].ExpectRead(0xFFFF).ExpectWrite(0xFFFE);  // GPIO_PULLSEL3 bit 0 to down (0).
  EXPECT_OK(gpio.GpioImplConfigIn(32, GPIO_PULL_DOWN));
  gpios_mock.VerifyAll();
}

TEST(GpioTest, NoPullIoCfg) {
  fbl::Array<ddk_mock::MockMmioReg> gpio_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kGpioRegCount], kGpioRegCount);
  fbl::Array<ddk_mock::MockMmioReg> iocfg_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kIoCfgRegCount], kIoCfgRegCount);
  ddk_mock::MockMmioRegRegion gpios_mock(gpio_regs.data(), sizeof(uint16_t), kGpioRegCount);
  ddk_mock::MockMmioRegRegion iocfg_mock(iocfg_regs.data(), sizeof(uint16_t), kIoCfgRegCount);
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), 1);  // Fake non-zero size.
  Mt8167GpioDeviceTest gpio(gpios_mock, iocfg_mock, unused);

  gpios_mock[0x330].ExpectRead(0xFFFF).ExpectWrite(0xFFF8);  // 3 GPIO_MODE4 bits for GPIO mode.
  gpios_mock[0x000].ExpectRead(0xFFFF).ExpectWrite(0x7FFF);  // GPIO_DIR1 bit 15 to input (0).
  iocfg_mock[0x560].ExpectRead(0xFFFF).ExpectWrite(0xFFFC);  // PUPD_CTRL6 bits 0-1 to no pull (0).
  EXPECT_OK(gpio.GpioImplConfigIn(15, GPIO_NO_PULL));
  gpios_mock.VerifyAll();
  iocfg_mock.VerifyAll();
}

TEST(GpioTest, PullUpIoCfg) {
  fbl::Array<ddk_mock::MockMmioReg> gpio_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kGpioRegCount], kGpioRegCount);
  fbl::Array<ddk_mock::MockMmioReg> iocfg_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kIoCfgRegCount], kIoCfgRegCount);
  ddk_mock::MockMmioRegRegion gpios_mock(gpio_regs.data(), sizeof(uint16_t), kGpioRegCount);
  ddk_mock::MockMmioRegRegion iocfg_mock(iocfg_regs.data(), sizeof(uint16_t), kIoCfgRegCount);
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), 1);  // Fake non-zero size.
  Mt8167GpioDeviceTest gpio(gpios_mock, iocfg_mock, unused);

  gpios_mock[0x330].ExpectRead(0xFFFF).ExpectWrite(0xFFF8);  // 3 GPIO_MODE4 bits for GPIO mode.
  gpios_mock[0x000].ExpectRead(0xFFFF).ExpectWrite(0x7FFF);  // GPIO_DIR1 bit 15 to input (0).
  iocfg_mock[0x560].ExpectRead(0x0000).ExpectWrite(0x0001);  // PUPD_CTRL6 bits 0-1 pull 10K (1).
  iocfg_mock[0x560].ExpectRead(0xFFFF).ExpectWrite(0xFFFB);  // PUPD_CTRL6 bit 2 to pull up (0).
  EXPECT_OK(gpio.GpioImplConfigIn(15, GPIO_PULL_UP));
  gpios_mock.VerifyAll();
  iocfg_mock.VerifyAll();
}

TEST(GpioTest, PullDownIoCfg) {
  fbl::Array<ddk_mock::MockMmioReg> gpio_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kGpioRegCount], kGpioRegCount);
  fbl::Array<ddk_mock::MockMmioReg> iocfg_regs =
      fbl::Array(new ddk_mock::MockMmioReg[kIoCfgRegCount], kIoCfgRegCount);
  ddk_mock::MockMmioRegRegion gpios_mock(gpio_regs.data(), sizeof(uint16_t), kGpioRegCount);
  ddk_mock::MockMmioRegRegion iocfg_mock(iocfg_regs.data(), sizeof(uint16_t), kIoCfgRegCount);
  ddk_mock::MockMmioRegRegion unused(nullptr, sizeof(uint16_t), 1);  // Fake non-zero size.
  Mt8167GpioDeviceTest gpio(gpios_mock, iocfg_mock, unused);

  gpios_mock[0x330].ExpectRead(0xFFFF).ExpectWrite(0xFFF8);  // 3 GPIO_MODE4 bits for GPIO mode.
  gpios_mock[0x000].ExpectRead(0xFFFF).ExpectWrite(0x7FFF);  // GPIO_DIR1 bit 15 to input (0).
  iocfg_mock[0x560].ExpectRead(0x0000).ExpectWrite(0x0001);  // PUPD_CTRL6 bits 0-1 pull 10K (1).
  iocfg_mock[0x560].ExpectRead(0x0000).ExpectWrite(0x0004);  // PUPD_CTRL6 bit 2 to pull down (1).
  EXPECT_OK(gpio.GpioImplConfigIn(15, GPIO_PULL_DOWN));
  gpios_mock.VerifyAll();
  iocfg_mock.VerifyAll();
}
}  // namespace gpio
